using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;

using System.Configuration;

using DAL.sql2005.common;

namespace ORM
{
    public enum foreignKeyFormat
    {
        field,
        property
    }

    public class ObjectCreator
    {

        #region constants

        public string TemplateDirectory
        {
            get
            {
                return ConfigurationManager.AppSettings["TemplateDirectory"];
            }
        }

        public string C_MANAGER_TEMPLATE
        {
            get
            {
                return TemplateDirectory + @"\Manager\baseManagerTemplate.tmpl";
            }

        }

        private string C_OBJECT_TEMPLATE
        {
            get
            {
                return TemplateDirectory + @"\Object\baseObjectTemplate.tmpl";
            }
        }

        private const string C_GET_TABLECOLUMNS = "prOrm_TableColumns_Select";
        private const string C_GET_FOREIGN_KEYS = "prOrm_ForeignKeys_Select";

        private const string C_PROPERTY_CONSTRUCT_STRING = "" +
            "public {8} {9}\r\n" +
            "{{\r\n" +
            "\tget\r\n" +
            "\t{{\r\n" +
            "\t\t{10}\r\n" +
            "\t}} \r\n" +
            "\tset\r\n" +
            "\t{{\r\n" +
            "\t\t{11}\r\n" +
            "\t}}\r\n" +
            "}}\r\n\r\n";


        //                    "|| f_{1} == null)\r\n" +

        private const string C_PROPERTY_GET_IMPLEMENTATION = "" +
            "if(f_{2} == null)\r\n" +
            "{{\r\n" +
            "\tif(f_{1} < 1 {3})" +
            "\t\treturn null;\r\n" +
            "\telse\r\n" +
            "\t\tf_{2} =  ManagerFactory<IManager<{0}>, {0}Manager>.GetManager().GetById((int)f_{1});\r\n" +
            "}}\r\n" +
            "\treturn f_{2};\r\n";

        private const string C_PROPERTY_SET_IMPLEMENTATION = "" +
            "\t\tf_{2} = value;\r\n";


        #endregion

        private string f_TableName;
        private DataTable f_Columns;
        private DataTable f_ForeignKeys;

        /// <summary>
        /// Initializes a new instance of the <see cref="ObjectCreator"/> class.
        /// </summary>
        /// <param name="tableName">Name of the table.</param>
        public ObjectCreator(string tableName, string dbName)
        {
            f_TableName = tableName;

            DataAccessManager db = new DataAccessManager(dbName);

            f_Columns = db.GetDataTableFromStoredProcedure(C_GET_TABLECOLUMNS, f_TableName);
            f_ForeignKeys = db.GetDataTableFromStoredProcedure(C_GET_FOREIGN_KEYS, f_TableName);
            db.Dispose();
            db = null;
        }

        /// <summary>
        /// Creates the manager class.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
        public void CreateManagerClass(string filename)
        {
            string f_TemplateString = "";
            TextReader sIn = new StreamReader(C_MANAGER_TEMPLATE);
            f_TemplateString = sIn.ReadToEnd();
            sIn.Close();
            sIn = null;

            string sval = String.Format(@f_TemplateString,
                f_TableName,
                f_TableName.ToUpper(),
                BuildColumnBasedString("list[\"{1}\"]", true, false, true),
                BuildColumnBasedString("\t\t\t\t{12}{0}ToAction.{7}{6}\r\n", false),//BuildColumnBasedString("\t\t\t{12} {0}ToAction.{7}{6}\r\n",  false),
                f_Columns.Rows.Count.ToString()
                );

            //ProductGroupToAction.FulfilmentBureau == null ? null 
            StreamWriter FileOut = new StreamWriter(filename);
            FileOut.Write(sval);
            FileOut.Close();
            FileOut = null;

            Console.Write(sval);
        }

        /// <summary>
        /// Creates the business object class.
        /// </summary>
        /// <param name="filename">The filename.</param>
        public void CreateBusinessObjectClass(string filename)
        {
            string f_TemplateString = "";
            TextReader sIn = new StreamReader(C_OBJECT_TEMPLATE);
            f_TemplateString = sIn.ReadToEnd();
            sIn.Close();
            sIn = null;

            string sval = String.Format(@f_TemplateString,
                f_TableName,
                BuildColumnBasedString("\tprivate {4} f_{2};\r\n", false),
                BuildColumnBasedString(C_PROPERTY_CONSTRUCT_STRING, false, false, false),
                BuildColumnBasedString("{1} == (({0}){0}ToCompare).{1}\r\n"),
                BuildColumnBasedString("\t\t{4} {3}{6}\r\n", false),
                BuildColumnBasedString("\t\t\tf_{2} = {3}; \r\n", false),
                BuildColumnBasedString("\tprivate {8} f_{9};\r\n", false, true, false)
                );

            StreamWriter FileOut = new StreamWriter(filename);
            FileOut.Write(sval);
            FileOut.Close();
            FileOut = null;


            Console.Write(sval);
        }


        /// <summary>
        /// Builds the column based string.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <returns></returns>
        public string BuildColumnBasedString(string format)
        {
            return BuildColumnBasedString(format, true);
        }

        /// <summary>
        /// Builds the column based string.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <param name="includePrimaryKeyColumn">if set to <c>true</c> [include primary key column].</param>
        /// <returns></returns>
        public string BuildColumnBasedString(string format, bool includePrimaryKeyColumn)
        {
            return BuildColumnBasedString(format, includePrimaryKeyColumn, false, false);
        }

        /// <summary>
        /// Builds the column based string.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <param name="includePrimaryKeyColumn">if set to <c>true</c> [include primary key column].</param>
        /// <param name="foreignKeysOnly">if set to <c>true</c> [foreign keys only].</param>
        /// <param name="convert">if set to <c>true</c> [convert].</param>
        /// <returns></returns>
        public string BuildColumnBasedString(string format, bool includePrimaryKeyColumn, bool foreignKeysOnly, bool convert)
        {
            string ColumnsString = string.Empty;

            string _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12;//, _13;
            #region reference
            // _0 = TableName
            // _1 = name i.e FK_ImageAssetId
            // _2 = FormattedNameNonCase i.e ImageAssetId
            // _3 = FormattedName i.e. imageAsset
            // _4 = type i.e int
            // _5 = current column index
            // _6 = end of line string
            // _7 = if its a foreign key format it as one
            // _8 = the foreign key type
            // _9 = the name of a foreign key object
            //_10 = property get implimentations for foeign key  
            //_11 = property set implimentations for foeign key
            //_12 = Database type -- NOT SET ANYWHERE?
            //_13 = isColumnNullable
            #endregion
            StringBuilder sb = new StringBuilder();
            int count = 0;

            foreach (DataRow dr in f_Columns.Rows)
            {
                if (!(!Convert.ToBoolean(dr["ForeignKey"])
                    &&
                    foreignKeysOnly))
                {
                    if (!(dr["name"].ToString().ToLower() == (f_TableName + "Id").ToLower() &&
                        includePrimaryKeyColumn == false))
                    {
                        _0 = f_TableName;
                        _1 = dr["Name"].ToString();
                        _2 = dr["FormattedNameNonCase"].ToString();
                        _3 = dr["FormattedName"].ToString();//fields
                        _4 = dr["Type"].ToString() == "string" ? dr["Type"].ToString() : Convert.ToBoolean(dr["isNullable"]) ? dr["Type"].ToString() + "?" : dr["Type"].ToString();
                        _5 = count.ToString();
                        _6 = count == f_Columns.Rows.Count - 1 ? "" : ",";
                        _7 = Convert.ToBoolean(dr["ForeignKey"]) ? FormatForeignKey(_2) : _2;
                        _8 = GetForeignKeyType(_1, _4);
                        _9 = Convert.ToBoolean(dr["ForeignKey"]) ? FormatForeignKeyObject(_1) : _2;
                        _10 = Convert.ToBoolean(dr["ForeignKey"]) ? GetPropertyImplimentation("get", _8, _2, FormatForeignKeyObject(_1), Convert.ToBoolean(dr["IsNullable"])) : "return f_" + _1 + ";";
                        _11 = Convert.ToBoolean(dr["ForeignKey"]) ? GetPropertyImplimentation("set", _8, _2, FormatForeignKeyObject(_1), Convert.ToBoolean(dr["IsNullable"])) : "f_" + _1 + " = value;";
                        _12 = Convert.ToBoolean(dr["IsNullable"]) ?
                            FormatNullableType(_0 + "ToAction." + _9, _4)
                            : string.Empty;

                        //ProductImageToAction.ThumbnailCustomImage == null ? null : ProductImageToAction.ThumbnailCustomImage.Id,
                        if (!convert)
                        {
                            sb.Append(
                                string.Format(format, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12)
                                );
                        }
                        else
                        {
                            sb.Append(
                                    string.Format(
                                        GetConversionStringByDBType(_4, Convert.ToBoolean(dr["isNullable"]), true),
                                          string.Format(format, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12)
                                    ) + _6 + "\r\n"
                                );

                        }
                    }
                }
                count = count + 1;

            }
            sb.Append("\r\n");
            ColumnsString = sb.ToString();
            sb = null;

            return ColumnsString;
        }

        /// <summary>
        /// Formats the foreign key.
        /// </summary>
        /// <param name="foreignKeyName">Name of the foreign key.</param>
        /// <returns></returns>
        private string FormatForeignKey(string foreignKeyName)
        {
            string ToReturn;

            ToReturn = foreignKeyName.Replace("FK_", "");
            ToReturn = ToReturn.Replace("Id", ".Id");

            return ToReturn;
        }

        /// <summary>
        /// Formats the foreign key object.
        /// </summary>
        /// <param name="foreignKeyName">Name of the foreign key.</param>
        /// <returns></returns>
        private string FormatForeignKeyObject(string foreignKeyName)
        {
            string ToReturn;

            ToReturn = foreignKeyName.Replace("FK_", "");
            ToReturn = ToReturn.Replace("Id", "");

            return ToReturn;
        }

        private string FormatNullableType(string objectNameAsString, string dbType)
        {
            return objectNameAsString + " == null ? (" + dbType + ")null :";
        }

        /// <summary>
        /// Gets the property implimentation.
        /// </summary>
        /// <param name="getOrSet">The get or set.</param>
        /// <param name="type">The type.</param>
        /// <param name="obj">The obj.</param>
        /// <param name="idField">The id field.</param>
        /// <returns></returns>
        public string GetPropertyImplimentation(string getOrSet, string type, string obj, string idField, bool nullable)
        {
            string nullableStr = nullable ? "?" : "";

            if (getOrSet == "get")
            {
                string CheckForNull = "";

                if (nullable)
                {
                    CheckForNull = "|| f_{0} == null";
                    CheckForNull = String.Format(CheckForNull, obj);
                }

                return string.Format(C_PROPERTY_GET_IMPLEMENTATION, type, obj, idField, CheckForNull);//nullableStr);
            }

            string SetPrefix = "" +
                "if(value == null)\r\n" +
                "\t\t\tf_{1} = (int{3})null;\r\n" +
                "\r\n";

            string SetImplementation = nullable ? SetPrefix + C_PROPERTY_SET_IMPLEMENTATION : C_PROPERTY_SET_IMPLEMENTATION;

            //if(!nullable)

            return string.Format(SetImplementation, type, obj, idField, nullableStr);
        }

        /// <summary>
        /// Gets the type of the foreign key.
        /// </summary>
        /// <param name="foreignKey">The foreign key.</param>
        /// <returns>the type of foreign key object as a string - or an empty string if it's not a foreign key</returns>
        private string GetForeignKeyType(string foreignKey, string columnType)
        {
            foreach (DataRow r in f_ForeignKeys.Rows)
            {
                if (r["COLUMN_NAME"].ToString().ToLower() == foreignKey.ToLower())
                    return r["REF_TABLE_NAME"].ToString();
            }

            return columnType;
        }

        /// <summary>
        /// Gets the type of the conversion string by DB.
        /// </summary>
        /// <param name="dbType">Type of the db.</param>
        /// <returns></returns>
        private string GetConversionStringByDBType(string dbType, bool isNullable, bool usePrefix)
        {
            string Prefix, ReturnString;

            //list[3] == DBNull.Value ? null : list[3].ToString(),

            switch (dbType.ToLower().Replace("?", ""))
            {
                case "string":
                    ReturnString = "{0}.ToString()";
                    break;
                case "int":
                    ReturnString = "Convert.ToInt32({0})";
                    break;
                case "bool":
                    ReturnString = "Convert.ToBoolean({0})";
                    break;
                case "datetime":
                    ReturnString = "Convert.ToDateTime({0})";
                    break;
                case "decimal":
                    ReturnString = "Convert.ToDecimal({0})";
                    break;
                case "char":
                    ReturnString = "Convert.ToChar({0})";
                    break;
                case "guid":
                    ReturnString = "new Guid({0})";
                    break;
                default:
                    ReturnString = "{0}.ToString()";
                    break;
            }

            if (isNullable &&
                dbType != "string")
                Prefix = "\t\t\t\t{0} == DBNull.Value ? (" + dbType + ")null :";
            else
                Prefix = "\t\t\t\t";

            if (!usePrefix) Prefix = string.Empty;

            ReturnString = Prefix + ReturnString;

            return ReturnString;
        }

    }
}
